//
// Created on 2023/6/7.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "video_encoder.h"
#include "avcodec_log.h"
#include <multimedia/av_codec/native_avcodec_videoencoder.h>
#include <multimedia/av_codec/native_avformat.h>
#include <multimedia/av_codec/native_avmemory.h>
#include <multimedia/player_framework/native_avcodec_videoencoder.h.>
#include <multimedia/player_framework/native_averrors.h>
#include <multimedia/player_framework/native_avmemory.h>
#include "multimedia/player_framework/native_avformat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include "avcodec_napi_utils.h"
#include "video_encoder_napi.h"
namespace OHOS {
namespace Media {

std::mutex VideoEncoder::mutex_;
std::map<OH_AVCodec*, VideoEncoder*> VideoEncoder::encoderMap_;
VideoEncoder::VideoEncoder(std::string mime){
    AVCODEC_LOGD("VideoEncoder create mime =%s",mime.c_str());
    venc_ = OH_VideoEncoder_CreateByMime(mime.c_str());
    if(venc_ == nullptr){
        AVCODEC_LOGD("venc_ is null");
    }
    setEncoderCallback();
    setVideoEncoderMap(venc_,this);
}
void VideoEncoder::setVideoEncoderMap(OH_AVCodec* codec,VideoEncoder* encoder){
    std::lock_guard<std::mutex> lock(mutex_);
    encoderMap_[codec] = encoder;
}
void VideoEncoder::delVideoEncoderMap(OH_AVCodec* codec){
    std::lock_guard<std::mutex> lock(mutex_);
    AVCODEC_LOGD("%s",__FUNCTION__);
    auto it = encoderMap_.find(codec);
    if (it != encoderMap_.end()) {
        encoderMap_.erase(it);
    }
}
VideoEncoder::~VideoEncoder(){
    AVCODEC_LOGD("VideoEncoder delete");
    delVideoEncoderMap(venc_);
}
int32_t VideoEncoder::configure(AvFormat *avFormat){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    OH_AVFormat * format = convertAvFormatToOHFormat(avFormat);
    int32_t ret = OH_VideoEncoder_Configure(venc_, format);
    return ret;
}
int32_t VideoEncoder::prepare(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    return OH_VideoEncoder_Prepare(venc_);
}
int32_t VideoEncoder::start(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    return OH_VideoEncoder_Start(venc_);
}
int32_t VideoEncoder::stop(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    return OH_VideoEncoder_Stop(venc_);    
}
int32_t VideoEncoder::flush(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    return OH_VideoEncoder_Flush(venc_);  
}

int32_t VideoEncoder::rest(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    return OH_VideoEncoder_Reset(venc_);    
}

void VideoEncoder::SetCallback(std::shared_ptr<AVCodecCallback> cb){
    AVCODEC_LOGD("%s",__FUNCTION__);
    avcodecCb = cb;
}
void VideoEncoder::pushInputBufferToQueue(int32_t index, OH_AVMemory *buffer){
    std::lock_guard<std::mutex> lock(queueMutex_);
    indexQueue.push(index);
    inBufferQueue.push(buffer);
}
int32_t VideoEncoder::sendFrameData(uint8_t *data, int32_t size, int32_t timestamp){
    AVCODEC_LOGD("sendFrameData data = %d",data);
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    std::lock_guard<std::mutex> lock(queueMutex_);
    if(indexQueue.size() <= 0){
        AVCODEC_LOGE("indexQueue is empty");
        return AV_ERR_NO_MEMORY;
    }
    uint32_t index = indexQueue.front();
    auto buffer = inBufferQueue.front();
    OH_AVCodecBufferAttr info;
    if(size == 0){
        info.size = 0;
        info.offset = 0;
        info.pts = 0;
        info.flags = AVCODEC_BUFFER_FLAGS_EOS;
        OH_VideoEncoder_PushInputData(venc_, index, info);
        AVCODEC_LOGE("send data len == 0");
    }
    info.size = size;
    info.offset = 0;
    info.pts = timestamp;
    memcpy(OH_AVMemory_GetAddr(buffer), data, OH_AVMemory_GetSize(buffer));
    AVCODEC_LOGD("[PerformanceTest] sendFrameData memory copy success ");
    int32_t ret = OH_VideoEncoder_PushInputData(venc_,index,info);
    indexQueue.pop();
    inBufferQueue.pop();
    return ret;
}

int32_t VideoEncoder::release(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    return OH_VideoEncoder_Destroy(venc_);        
}
std::string VideoEncoder::getOutputDescription(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, "venc_ is null,no output description","Fatal: OH_VideoEncoder_CreateByMime fail");
    OH_AVFormat* format =  OH_VideoEncoder_GetOutputDescription(venc_);
    std::string info = OH_AVFormat_DumpInfo(format);
    return info;
}

VideoEncoder* VideoEncoder::GetVideoEncoder(OH_AVCodec *codec){
    std::lock_guard<std::mutex> lock(mutex_);
    if (encoderMap_.find(codec) != encoderMap_.end()) {
        return encoderMap_.at(codec);
    }
    return nullptr;
}

int32_t VideoEncoder::setEncoderCallback(){
    AVCODEC_CHECK_AND_RETURN_RET_LOG(venc_ != nullptr, AV_ERR_UNKNOWN,"Fatal: OH_VideoEncoder_CreateByMime fail");
    struct OH_AVCodecAsyncCallback callback;
                callback.onError = VideoEncoder::OnError;
                callback.onStreamChanged = VideoEncoder::OnStreamChanged;
                callback.onNeedInputData = VideoEncoder::OnNeedInputData;
                callback.onNeedOutputData = VideoEncoder::OnNewOutputData;
    return OH_VideoEncoder_SetCallback(venc_, callback,nullptr);
}
void VideoEncoder::OnError(OH_AVCodec *codec, int32_t errorCode, void *userData){
    AVCODEC_LOGE("OnError %d",errorCode);
    VideoEncoder* encoder = GetVideoEncoder(codec);
    if (encoder != nullptr) {
        encoder->avcodecCb->OnCodecError(errorCode);
    }
}
void VideoEncoder::OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData){
    AVCODEC_LOGD("%s:format = %s ",__FUNCTION__,OH_AVFormat_DumpInfo(format));
    VideoEncoder* encoder = GetVideoEncoder(codec);
    if (encoder != nullptr) {
        AvFormat* avFormat = convertOHFormatToAvFormat(format);
        encoder->avcodecCb->OnOutputFormatChanged(avFormat);
    }
}

void VideoEncoder::OnNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData){
    VideoEncoder* encoder = GetVideoEncoder(codec);
    if (encoder != nullptr) {
        encoder->pushInputBufferToQueue(index,data);
    }
    
}
void VideoEncoder::OnNewOutputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data,OH_AVCodecBufferAttr *attr, void *userData){
    VideoEncoder* encoder = GetVideoEncoder(codec);
    if (encoder != nullptr) {
        encoder->avcodecCb->OnOutputBufferAvailable(OH_AVMemory_GetAddr(data), attr->size, attr->pts, attr->flags);
       // encoder->avcodecCb->OnInputBufferAvailable(OH_AVMemory_GetAddr(data), attr->size);
    }
    OH_VideoEncoder_FreeOutputData(codec,index);
}

OH_AVFormat* VideoEncoder::convertAvFormatToOHFormat(AvFormat* avFormat){
    OH_AVFormat *format = OH_AVFormat_Create();
    OH_AVFormat_SetStringValue(format,OH_MD_KEY_CODEC_MIME,avFormat->codec_mime.c_str());
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, avFormat->width);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT,avFormat->height);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, avFormat->pixel_format);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_FRAME_RATE, avFormat->frame_rate);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_BITRATE,avFormat->bitrate);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE,avFormat->codec_profile);
    OH_AVFormat_SetIntValue(format, "codec_quality",avFormat->codec_quality);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE,avFormat->video_encode_bitrate_mode);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL,avFormat->i_frame_interval);
    return format;
}

AvFormat* VideoEncoder::convertOHFormatToAvFormat(OH_AVFormat* format){
    AvFormat avFormat;
    const char* codec_mime;
    if(OH_AVFormat_GetStringValue(format,OH_MD_KEY_CODEC_MIME,&codec_mime)){
        avFormat.codec_mime = codec_mime;
    }
    int32_t width;
    if(OH_AVFormat_GetIntValue(format, OH_MD_KEY_WIDTH,&width)){
        avFormat.width = width;
    }
    int32_t height;
    if(OH_AVFormat_GetIntValue(format, OH_MD_KEY_HEIGHT,&height)){
        avFormat.height = height;
    }
    int32_t pixel_format;
    if(OH_AVFormat_GetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, &pixel_format)){
        avFormat.pixel_format = pixel_format;
    }
    int32_t frame_rate;
    if(OH_AVFormat_GetIntValue(format, OH_MD_KEY_FRAME_RATE, &frame_rate)){
        avFormat.frame_rate = frame_rate;
    }
    int32_t bitrate;
    if(OH_AVFormat_GetIntValue(format,OH_MD_KEY_BITRATE,&bitrate)){
        avFormat.bitrate = bitrate;
    }
    int32_t codec_profile;
    if(OH_AVFormat_GetIntValue(format, OH_MD_KEY_PROFILE, &codec_profile)){
        avFormat.codec_profile = codec_profile;
    }
    int32_t codec_quility;
    if(OH_AVFormat_GetIntValue(format, "codec_quality", &codec_quility)){
        avFormat.codec_quality = codec_quility;
    }
    int32_t i_frame_interval;
    if(OH_AVFormat_GetIntValue(format,OH_MD_KEY_I_FRAME_INTERVAL,&i_frame_interval)){
        avFormat.i_frame_interval = i_frame_interval;
    }
    return &avFormat;
}

std::shared_ptr<VideoEncoder> VideoEncoderFactory::CreateByMime(const std::string mime){
    AVCODEC_LOGD("VideoEncoderFactory::CreateByMime mime = %s",mime.c_str());
    std::shared_ptr<VideoEncoder> encoder = std::make_shared<VideoEncoder>(mime);
    CHECK_AND_RETURN_RET_LOG(encoder != nullptr, nullptr, "failed to new VideoEncoder");
    return encoder;
}

} // namespace Media
} // namespace OHOS